package com.cyd.couponapp.service.dubbo;

import com.alibaba.fastjson.JSON;
import com.cyd.couponapp.constant.Constant;
import com.cyd.couponapp.domain.TCoupon;
import com.cyd.couponapp.domain.TCouponExample;
import com.cyd.couponapp.domain.TUserCoupon;
import com.cyd.couponapp.domain.TUserCouponExample;
import com.cyd.couponapp.mapper.TCouponMapper;
import com.cyd.couponapp.mapper.TUserCouponMapper;
import com.cyd.couponapp.util.SnowflakeIdWorker;
import com.cyd.couponserviceapi.dto.CouponDto;
import com.cyd.couponserviceapi.dto.CouponNoticeDto;
import com.cyd.couponserviceapi.dto.UserCouponDto;
import com.cyd.couponserviceapi.dto.UserCouponInfoDto;
import com.cyd.couponserviceapi.dto.service.ICouponService;
import com.cyd.userserviceapi.service.IUserService;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CouponService implements ICouponService {
    private static final Logger logger = LoggerFactory.getLogger(CouponService.class);

    @Resource
    private TCouponMapper tCouponMapper;

    @Resource
    private TUserCouponMapper tUserCouponMapper;


     //注意这是引用远程的配置的注解，并且pom要引用user-service-api模块jar
    @Reference
    private IUserService iUserService;

    @Resource
    private RedisTemplate redisTemplate;


    private  static String COUPON="couponSet";

    private  static final  int COUPON_NUM=10;


    //***************************************批量获取优惠券  start
    public List<TCoupon> getCouponListByIds(String ids)  {
        logger.info("批量获取优惠券 "+ids);
        List<TCoupon> tCoupons=Lists.newArrayList();
        List<Integer> loadFromDB=Lists.newArrayList();

        String[] idsstr=ids.split(",");
        List<String> idslist=Lists.newArrayList(idsstr);
        for (String id : idslist) {
               TCoupon  tCoupon=couponidsCache.getIfPresent(id);
            if(null==tCoupon){
                loadFromDB.add(Integer.parseInt(id.toString()));
            }else{
                tCoupons.add(tCoupon);
            }
        }
        List<TCoupon> tCoupons1=couponByIds(loadFromDB);
        //1.8新写法
        Map<Integer,TCoupon> map=tCoupons1.stream().collect(Collectors.toMap(TCoupon::getId, Tcoupon->Tcoupon));
         tCoupons.addAll(tCoupons1);
         couponidsCache.putAll(map);
        return tCoupons;
    }
    private List<TCoupon> couponByIds(List<Integer> ids) {
        TCouponExample example=new TCouponExample();
        example.createCriteria().andIdIn(ids);
        return tCouponMapper.selectByExample(example);
    }


    LoadingCache<Integer, TCoupon> couponidsCache= CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)    //过期时间 10分钟
            .refreshAfterWrite(5, TimeUnit.MINUTES)   //隔多久异步刷新
            .build(new CacheLoader<Integer, TCoupon>() {
                @Override
                public TCoupon load(Integer o) throws Exception {
                    return loadIdCoupon(o);
                }
            });

    private TCoupon loadIdCoupon(Integer id) {
        return tCouponMapper.selectByPrimaryKey(id);
    }
    //***************************************批量获取优惠券  end


    //**************************************优惠券列表 start
    /**
     * 优惠券列表
     * @return
     */
    @Override
    public List<CouponDto> getCouponList(){
        List<TCoupon> tCoupons=Lists.newArrayList();
        List<CouponDto> couponDtos=Lists.newArrayList();
        try {
            tCoupons=couponCache.get(1);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        tCoupons.forEach(tCoupon -> {
            CouponDto dto=new CouponDto();
            BeanUtils.copyProperties(tCoupon,dto);
            couponDtos.add(dto);
        });
        return couponDtos;
    }

    LoadingCache<Integer, List<TCoupon>> couponCache= CacheBuilder.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)    //过期时间 10分钟
            .refreshAfterWrite(5, TimeUnit.MINUTES)   //隔多久异步刷新
            .build(new CacheLoader<Integer, List<TCoupon>>() {
                @Override
                public List<TCoupon> load(Integer o) throws Exception {
                    return loadCoupon(o);
                }
            });

    public List<TCoupon> loadCoupon(Integer o) {
        TCouponExample example=new TCouponExample();
        example.createCriteria().andStatusEqualTo(Constant.USERFUL)
                .andStartTimeLessThan(new Date()).andEndTimeGreaterThan(new Date());
        return tCouponMapper.selectByExample(example);
    }


    //**************************************优惠券列表 end

    //*********************优惠券定时任务 start
    private  Map  couponMap=new ConcurrentHashMap();

      public void updateCouponMap(){
          Map couponMap1 = new ConcurrentHashMap();
          List<TCoupon> tCoupons = Lists.newArrayList();
          try{
              tCoupons= this.loadCoupon(1);
              couponMap1.put(1,tCoupons);
              couponMap = couponMap1;
              logger.info("update coupon list:{},coupon list size:{}", JSON.toJSONString(tCoupons),tCoupons.size());
          }catch (Exception e){
              logger.error("update coupon list:{},coupon list size:{}",JSON.toJSONString(tCoupons),tCoupons.size(),e);
          }
      }

    public List<TCoupon> getCouponList4Map(){

          return (List<TCoupon>) couponMap.get(1);
    }


    //*********************优惠券定时任务 end


    //*****************************Caffeine start
    com.github.benmanes.caffeine.cache.LoadingCache<Integer,List<TCoupon>> couponCaffeine = Caffeine.newBuilder()
            .expireAfterWrite(10,TimeUnit.MINUTES)
            .refreshAfterWrite(5,TimeUnit.MINUTES)
            .build(new com.github.benmanes.caffeine.cache.CacheLoader<Integer, List<TCoupon>>() {
                @Override
                public List<TCoupon> load(Integer o) throws Exception {
                    return loadCoupon(o);
                }
            });


    /***
     * 获取有效时间的可用优惠券列表
     * // 1、是否存在远程调用 HTTP、RPC Metrics
     * // 2、大量内存处理  list.contain() ==>set.contain
     * @return
     */
    public List<TCoupon> getCouponListCaffeine(){
        List<TCoupon> tCoupons = Lists.newArrayList();
        try {
            tCoupons =  couponCaffeine.get(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tCoupons;
    }
    //*****************************Caffeine end


    //*****************************用户获取优惠券方法 start
     @Override
     public String saveUserCoupon(UserCouponDto dto){
        String result=checkCoupon(dto);
        if(result!=null){
            return result;
        }
        TCoupon coupon=tCouponMapper.selectByPrimaryKey(dto.getCouponId());
        if(coupon==null){
            return "conpon无效";
        }
        return save2DB(dto,coupon);
     }

    private String checkCoupon(UserCouponDto dto){
         Integer couponId=dto.getCouponId();
         Integer userId=dto.getUserId();
         if(couponId==null || userId==null){
             return "couponId 或者 userId为空";
         }
         return null;
     }

     private  String save2DB(UserCouponDto dto,TCoupon coupon){
         TUserCoupon userCoupon=new TUserCoupon();
         BeanUtils.copyProperties(dto,userCoupon);

         userCoupon.setPicUrl(coupon.getPicUrl());
         userCoupon.setCreateTime(new Date());
         SnowflakeIdWorker worker=new SnowflakeIdWorker(0,0);
         userCoupon.setUserCouponCode(worker.nextId()+"");
         tUserCouponMapper.insertSelective(userCoupon);
         logger.info("领取优惠券成功");
        return "领取优惠券成功";
     }
    //*****************************用户获取优惠券方法 end



    //***********************************************用户获取自己的优惠券 start
    @Override
    public List<UserCouponInfoDto> userCouponList(Integer userId) {
        List<UserCouponInfoDto> dtos=Lists.newArrayList();
        if(userId==null){
            return dtos;
//              return new ArrayList<UserCouponInfoDto>();
        }
        //查询用户未使用的优惠券
        List<TUserCoupon> userCoupon= getUserCoupon(userId);
        if(CollectionUtils.isEmpty(userCoupon)){
            return dtos;
        }
        //获取用户优惠券map结构
        Map<Integer,TCoupon> idCouponMap=getCoponMap(userCoupon);
        //组装UserCouponInfoDto
        return  warpCoupon(userCoupon,idCouponMap);
    }

    /**
     *  查询用户未使用的优惠券
     * @param userId
     * @return
     */
    private List<TUserCoupon> getUserCoupon(Integer userId){

        TUserCouponExample example=new TUserCouponExample();
        example.createCriteria()
                .andUserIdEqualTo(userId)
                .andStatusEqualTo(0);
        List<TUserCoupon> userCoupon=tUserCouponMapper.selectByExample(example);
        return userCoupon;
    }

    /**
     *获取用户优惠券map结构
     * @param userCoupon
     * @return
     */
    private   Map<Integer,TCoupon> getCoponMap(List<TUserCoupon> userCoupon){
        //获取优惠券ID SET 集合
        Set<Integer> couponIds=getCouponIds(userCoupon);
        //组装coupon  从缓存拿数据
        List<TCoupon> coupons=getCouponListByIds(StringUtils.join(couponIds,","));
        Map<Integer,TCoupon> idCouponMap=couponList2Map(coupons);
        return idCouponMap;
    }

    /**
     * 获取couponIds
     * @param userCoupons
     */
    private  Set<Integer> getCouponIds( List<TUserCoupon> userCoupons){
        //JDK1.8MAP
        Set<Integer> couponIds=userCoupons.stream().map(userCoupon-> userCoupon.getCouponId()).collect(Collectors.toSet());
   /*     //老写法
        Set<Integer> couponIds1=new HashSet<>();
        for(TUserCoupon userCoupon:userCoupons){
            couponIds1.add(userCoupon.getCouponId());
        }*/
        return couponIds;
    }

    /**
     * 把coupons转成Map k=couponId,v=coupon
     * @param coupons
     * @return
     */
    private  Map<Integer,TCoupon> couponList2Map(List<TCoupon> coupons){
        return coupons.stream().collect(Collectors.toMap(o -> o.getId(),o -> o));
    }

    private List<UserCouponInfoDto> warpCoupon(List<TUserCoupon> userCoupons,Map<Integer,TCoupon>  idCouponMap){

        List<UserCouponInfoDto> userCouponInfoDtos=userCoupons.stream().map(userCoupon->{
            UserCouponInfoDto dto=new UserCouponInfoDto();
            int couponId=userCoupon.getCouponId();
            TCoupon coupon=idCouponMap.get(couponId);
            dto.setAchieveAmount(coupon.getAchieveAmount());
            dto.setReduceAmount(coupon.getReduceAmount());
            BeanUtils.copyProperties(userCoupon,dto);
            return dto;
        }).collect(Collectors.toList());
        logger.info("invoke get user coupon list,result:{}",JSON.toJSON(userCouponInfoDtos));
        return userCouponInfoDtos;
    }


    //*************************************************用户获取自己的优惠券 end

    //*********************公告 start

    /**
     *查询公告栏,前10条数据
      */
    @Override
    public  List<CouponNoticeDto> queryCouponNotice(){
         Set<String>   couponSet=redisTemplate.opsForZSet().reverseRange(COUPON,0,-1);
         //获取前N条
        List<String> userCouponStrs=couponSet.stream().limit(COUPON_NUM).collect(Collectors.toList());
        Map<String,String> couponUserMap=userCouponStrs.stream().collect(Collectors.toMap(o-> o.split("_")[1],o->o.split("_")[0]));
        List<String> couponIdS=userCouponStrs.stream().map(s-> s.split("_")[1]).collect(Collectors.toList());
        String couponIdStrs=StringUtils.join(couponIdS,",");
        List<TCoupon> couponlist=getCouponListByIds(couponIdStrs);//缓存拿数据

        List<CouponNoticeDto> dtos=couponlist.stream().map(coupon->{
            CouponNoticeDto dto=new CouponNoticeDto();
            BeanUtils.copyProperties(coupon,dto);
            dto.setUserId(Integer.parseInt(couponUserMap.get(coupon.getId()+"")));
            return dto;
        }).collect(Collectors.toList());
        return dtos;
     }

    /**
     * 优惠券核销的时候调用, zadd couponSet 1582044503921 1_101126
     */
    public void updateCoupon(String userCouponStr){
        redisTemplate.opsForZSet().add(COUPON,userCouponStr, System.currentTimeMillis());
        Set<String> couponSet=redisTemplate.opsForZSet().range(COUPON,0,-1);
        if(couponSet.size()>COUPON_NUM){
//            String removeCouponId=couponSet.stream().findFirst().get();
            //防止出现11个还是做个处理
            int removeCount=couponSet.size()-COUPON_NUM;
            Set<String> couponSet2=redisTemplate.opsForZSet().range(COUPON,0,removeCount-1);//索引从0开始所以要减去一个
            couponSet2.stream().forEach(key->{
                System.out.println(key);
                redisTemplate.opsForZSet().remove(COUPON,key);
            });
        }
    }


    /**
     * 更新coupon为已核销核销状态
     * @param orderId
     * @param userId
     */
    public  void payResult(int orderId,int userId){
        TUserCouponExample example=new TUserCouponExample();
        example.createCriteria().andUserIdEqualTo(userId)
                .andOrderIdEqualTo(orderId);
        List<TUserCoupon> tUserCoupons=tUserCouponMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(tUserCoupons)){
            logger.info("找不到订单编号:{}，userId",orderId,userId);
            return;
        }
        TUserCoupon userCoupon=tUserCoupons.get(0);
        userCoupon.setStatus(1);
        tUserCouponMapper.updateByPrimaryKeySelective(userCoupon);
    }


    /**
     * 更新订单状态
     * @param orderId
     * @param couponCode
     * @param userId
     */
    public  void saveOrder(int orderId,String couponCode,int userId){
        TUserCouponExample example=new TUserCouponExample();
        example.createCriteria().andUserCouponCodeEqualTo(couponCode).andOrderIdEqualTo(orderId);
        List<TUserCoupon> tUserCoupons=tUserCouponMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(tUserCoupons)){
            logger.info("找不到优惠券编码:{}",couponCode);
            return;
        }
        TUserCoupon userCoupon=tUserCoupons.get(0);
        userCoupon.setOrderId(orderId);
        userCoupon.setStatus(0);//未核销
        userCoupon.setUserId(userId);
        tUserCouponMapper.updateByPrimaryKeySelective(userCoupon);
    }


    //*********************公告 end


    /**
     * 根据用户ID查询用户信息
     * @param id
     * @return
     */
    public String getUserById(int id){
        return iUserService.getUserById(id).toString();
    }



  //测试
    public String query(){
        TCouponExample example = new TCouponExample();
        example.createCriteria().andCodeEqualTo("9510536e-1d68-4b3b-b7c9-9923503423f2");
        List<TCoupon> tCoupon =  tCouponMapper.selectByExample(example);
        return tCoupon.get(0).toString();
    }

//    Benchmark               Mode  Cnt    Score   Error  Units
//    JMHSpingBootTest.test  thrpt    2  752.269          ops/s


}
